/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package provider.impl.genomemap.data;

import com.google.common.collect.ImmutableList;
import commons.core.Simulator;
import commons.provider.ProviderException;
import commons.provider.impl.BaseProviderImpl;
import commons.util.MathUtil;
import genomemap.data.KSA00Data;
import genomemap.model.KSA00;
import genomemap.provider.Providers;

import java.util.ArrayList;
import java.util.List;

/**
 *
 *
 * @version 1.3.0
 * @author Susanta Tewari
 * @history Created on 1/7/13
 * @since 1.3.0
 */
public class KSA00DataSimulatorProviderImpl_1
        extends BaseProviderImpl<Simulator.DATA<KSA00, KSA00Data>>
        implements Providers.KSA00DataSimulatorProvider {

    /**  */
    private List<String> clones;

    /**  */
    private byte[][] data;

    /**
     * @param input
     * @param clone_count
     */
    private void simulate(final KSA00 input, final int clone_count) {

        final int probe_count               = input.getProbes().size();
        final double fale_positive          = input.getFalsePositiveProbability();
        final double false_negative         = input.getFalseNegativeProbability();
        final int length                    = input.getChromosomeLength();
        final double half_length            = length / 2.0;
        final ImmutableList<Double> spacing = input.getProbeSpacing();
        final double ch_length = MathUtil.sumDoublePrecision(spacing) + length * probe_count;

        data   = new byte[clone_count][probe_count];
        clones = new ArrayList<>(clone_count);

        for (int i = 0; i < clone_count; i++) {

            clones.add("Clone-" + i + 1);

            final double clone_pos = Math.random() * ch_length;
            double region          = spacing.get(0);    // left of the first one

            for (int j = 0; j < probe_count; j++) {

                final double right_end = region + 1.5 * length;
                final double left_end  = region - half_length;
                boolean probe_hit      = (clone_pos > left_end) && (clone_pos < right_end);

                if (probe_hit) {
                    if (Math.random() < false_negative) probe_hit = false;
                } else {
                    if (Math.random() < fale_positive) probe_hit = true;
                }

                if (probe_hit) data[i][j] = 1;

                region += length + spacing.get(j + 1);

                if (clone_pos < left_end) break;
            }
        }
    }

    @Override
    public Simulator.DATA<KSA00, KSA00Data> create() throws ProviderException {

        return new Simulator.DATA<KSA00, KSA00Data>() {

            @Override
            public Iterable<KSA00Data> simulate(final DataInput<KSA00> input, final int simCount) {

                final List<KSA00Data> result = new ArrayList<>();
                final KSA00 model            = input.getModel();
                final int sampleSize         = input.getSampleSize();
                final KSA00DataSimulatorProviderImpl_1 simulator =
                    new KSA00DataSimulatorProviderImpl_1();

                for (int i = 0; i < simCount; i++) {

                    simulator.simulate(model, sampleSize);
                    result.add(new KSA00Data(model, simulator.getClones(), simulator.getData()));
                }

                return result;
            }
        };
    }

    /**
     * @return
     */
    private List<String> getClones() {
        return clones;
    }

    /**
     * @return
     */
    private byte[][] getData() {
        return data;
    }

    @Override
    public Class<KSA00Data> getDataClass() {
        return KSA00Data.class;
    }

    @Override
    public Class<KSA00> getModelClass() {
        return KSA00.class;
    }
}
